package com.ms.jsf;

import com.ms.domain.viewmodel.FilterBase;
import com.ms.domain.viewmodel.FiltersBean;
import com.ms.exception.DpwcBusinessException;
import com.ms.exception.DpwcConfigException;
import com.ms.exception.DpwcSystemException;
import com.ms.exception.DpwcValidationException;
import com.ms.util.GenericUtil;
import com.ms.util.exporter.ExcelExporter;
import com.ms.util.exporter.PdfExporter;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.context.RequestContext;
import org.springframework.util.StringUtils;

import javax.annotation.PostConstruct;
import javax.faces.event.ActionEvent;
import javax.servlet.ServletException;
import java.io.IOException;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


/**
 * User: gmc
 * Date: 13/03/13
 */
public abstract class FindCrudBeanBase<Filter, Result> extends CrudBeanBase<Filter, Result> {

    protected Filter filter;
    protected List<Result> resultList;
    private Result selectedRow;
    private Result[] selectedRows;
    protected boolean creation = false;
    protected boolean readOnly = false;
    protected boolean showMsgNoRecordFound = true;


    @PostConstruct
    public abstract void init();

    protected void executeFindAction() {
        findAction(null);
    }

    public void findAction(ActionEvent actionEvent) {
        LOG.debug("Executing find action");
        cleanSelectedRowsVars();
        onFindAction();
        repaintResultPanel();
        if (resultList == null || resultList.size() == 0) {
            if (showMsgNoRecordFound) {
                addWarnMessage(ViewConstant.NO_RECORD_FOUND_MSG);
            }
        }
    }

    private void cleanSelectedRowsVars() {
        setSelectedRow(null);
        setSelectedRows(null);
    }

    protected void refreshResult() {
        cleanSelectedRowsVars();
        onFindAction();
        repaintResultPanel();
    }

    protected void repaintCriteriaPanel() {
        repaintUiComponent(getPanelCriteriaId());
    }

    private String getPanelCriteriaId() {
        return ViewConstant.PANEL_CRITERIA_ID + getPageSufix();
    }

    protected void repaintResultPanel() {
        repaintUiComponent(getPanelResultId());
    }

    private String getPanelResultId() {
        return ViewConstant.PANEL_RESULT_ID + getPageSufix();
    }

    protected void repaintUiComponent(String id) {
        RequestContext rcontext = RequestContext.getCurrentInstance();
        rcontext.update(id);
    }


    public void cleanAction(ActionEvent actionEvent) {
        onCleanAction();
        resultList = new ArrayList<Result>();
        repaintCriteriaPanel();
        repaintResultPanel();
    }

    protected void onCleanAction() {
        Class<?> filterClass = GenericUtil.getGenericFrom(this.getClass(), 0);
        try {
            filter = (Filter) filterClass.newInstance();
        } catch (Throwable e) {
            throw new DpwcSystemException("Problemas instanciando la clase:" + filterClass, e);
        }
    }

    @Override
    public void showNewPageAction(ActionEvent actionEvent) throws IOException, ServletException {
        String pageWV = (String) actionEvent.getComponent().getAttributes().get(ViewConstant.PAGE_WV);
        if (!StringUtils.hasText(pageWV)) {
            throw new DpwcConfigException("Debe enviar como parametro el valor url de la pagina");
        }
        showNewPage(pageWV);
    }

    public void showNewDialogAction(ActionEvent actionEvent) {
        creation = true;
        readOnly = false;
        createVmForCreation();
        showModal();
    }

    public void showViewDialogAction(ActionEvent actionEvent) {
        creation = false;
        readOnly = true;
        Result selectedRow = getSelectedRow();
        createVmForView(selectedRow);
        showModal();
    }

    protected void createVmForView(Result selectedRow) {
        createVmForEdition(selectedRow);
    }

    public void showEditDialogAction(ActionEvent actionEvent) {
        creation = false;
        readOnly = false;
        Result selectedRow = getSelectedRow();
        createVmForEdition(selectedRow);
        showModal();
    }

    public void deleteAction(ActionEvent actionEvent) {
        Result selectedRow = getSelectedRow();
        onDeleteAction(selectedRow);
        addInfoMessage("El registro fue eliminado.");
        refreshResult();
    }

    protected void createVmForEdition(Result selectedRow) {

    }

    protected void onDeleteAction(Result selectedRow) {

    }


    protected void createVmForCreation() {

    }

    public void saveAction(ActionEvent actionEvent) {
        onSaveAction();
        hideModal();
        refreshResult();
    }

    protected void onSaveAction() {
        throw new DpwcConfigException("Debe implementar el metodo:onSaveAction");
    }

    public void validateExistSelectedRow(ActionEvent actionEvent) {
        if (getSelectedRows().length == 0) {
            throw new DpwcValidationException("datatable.row.selected.requerido");
        }
        if (getSelectedRows().length > 1) {
            throw new DpwcValidationException("datatable.one.row.selected");
        }
    }

    public void validateExistSelectedRows(ActionEvent actionEvent) {
        if (getSelectedRows().length < 2) {
            throw new DpwcBusinessException("datatable.several.rows.selected");
        }
    }

    public Filter getFilter() {
        return filter;
    }

    public void setFilter(Filter filter) {
        this.filter = filter;
    }

    public List<Result> getResultList() {
        return resultList;
    }

    public void setResultList(List<Result> resultList) {
        this.resultList = resultList;
    }

    protected Result getSelectedRow() {
        // If selected row was set explicitly
        if (this.selectedRow != null) {
            return selectedRow;
        }
        // If selected row was sent as parameter
        Result selectedRow = (Result) actionEvent.getComponent().getAttributes().get(ViewConstant.SELECTED_ROW);
        if (selectedRow == null) {
            // If parent is datatable
            Object parent = actionEvent.getComponent().getParent().getParent();
            if (parent instanceof DataTable) {
                DataTable dataTable = (DataTable) parent;
                selectedRow = (Result) dataTable.getRowData();
            }
        }
        if (selectedRow == null) {
            // If there is selected rows then the first is return
            if (selectedRows != null && selectedRows.length > 0) {
                return selectedRows[0];
            }
        }
        if (selectedRow == null) {
            throw new DpwcConfigException("El parent no es de tipo DataTable. Ud tiene que enviar como parámetro el <selectedRow>.Ejem: <f:attribute name='selectedRow'...");
        }
        return selectedRow;
    }

    protected boolean allRowsWasSelected = false;

    // Metodo llamado por la tabla para forzar el seleccionar todos los registros
    public void selectUnselectAllRows() {
        if(!allRowsWasSelected){
            allRowsWasSelected = true;
            setSelectedRows(getResultListAsArray());
        }else {
            allRowsWasSelected = false;
            setSelectedRows(getResultEmptyAsArray());
        }
    }

    private Result[] getResultListAsArray() {
        Class<?> resultClass = GenericUtil.getGenericFrom(this.getClass(), 1);
        final Result[] resultArray = (Result[]) Array.newInstance(resultClass, resultList.size());
        for (int i=0;i<resultList.size();i++) {
            resultArray[i]=resultList.get(i);
        }
        return resultArray;
    }

    private Result[] getResultEmptyAsArray() {
        Class<?> resultClass = GenericUtil.getGenericFrom(this.getClass(), 1);
        final Result[] resultArray = (Result[]) Array.newInstance(resultClass, 0);
        return resultArray;
    }


    public void setSelectedRow(Result selectedRow) {
        this.selectedRow = selectedRow;
    }


    public boolean isCreation() {
        return creation;
    }

    public void setCreation(boolean creation) {
        this.creation = creation;
    }

    protected List<Result> getSelectedRowsAsList() {
        return Arrays.asList(selectedRows);
    }

    public Result[] getSelectedRows() {
        return selectedRows;
    }

    public void setSelectedRows(Result[] selectedRows) {
        this.selectedRows = selectedRows;
    }

    protected FiltersBean getFiltersBean() {
        FilterBase customfilter = (FilterBase) filter;
        return customfilter.getFiltersBean();
    }


    public void preProcessExportPdf(Object document) {
        PdfExporter.addFilters(document, getFiltersBean(), getResourceBundle());
    }

    public void preProcessExportExcel(Object document) {
        ExcelExporter.addFilters(document, getFiltersBean(), getResourceBundle());
    }


    public Integer getResultListSize() {
        return resultList != null ? resultList.size() : 0;
    }

    public boolean isShowablePaginator() {
        return getResultListSize() > ViewConstant.INIT_ROWS_PER_RESULT_PAGE;
    }

    public int getInitRowsPerResultPage() {
        return ViewConstant.INIT_ROWS_PER_RESULT_PAGE;
    }

    public boolean isReadOnly() {
        return readOnly;
    }

    public String getDlgHeaderLabel() {
        return creation ? "label_create" : (readOnly ? "label_view" : "label_edit");
    }
}
